using System;
using System.Security.Cryptography;
using System.Text;


namespace PW.ApplicationBlocks.Cryptography
{
   /// <summary>
   /// Easy use of TripleDES.
   /// </summary>
   public sealed class TripleDESUtil {
      private static readonly Encoding s_encoding = Encoding.UTF8;
      private static readonly TripleDESCryptoServiceProvider s_tripleDESCsp = new TripleDESCryptoServiceProvider();
      private static ICryptoTransform s_encryptor = null;
      private static ICryptoTransform s_decryptor = null;

      private TripleDESUtil() {}

      /// <summary>
      /// Sets the key used for encryption and decryption.
      /// </summary>
      /// <param name="key">Key to set.</param>
      /// <param name="iv">IV to set.</param>
      /// <remarks>
      /// Key must be 24 bytes length.
      /// IV must be 8 bytes length.
      /// </remarks>
      public static void SetKeyAndIV(byte[] key, byte[] iv) {
         if (key == null) {
            throw new ArgumentNullException("key");
         }
         if (iv == null) {
            throw new ArgumentNullException("iv");
         }
         if (key.Length != 24) {
            throw new ArgumentException("Key must be 24 bytes length.", "key");
         }
         if (iv.Length != 8) {
            throw new ArgumentException("IV must be 8 bytes length.", "iv");
         }

         s_tripleDESCsp.Key = key;
         s_tripleDESCsp.IV = iv;

         s_encryptor = s_tripleDESCsp.CreateEncryptor();
         s_decryptor = s_tripleDESCsp.CreateDecryptor();
      }

      /// <summary>
      /// Encrypt a byte buffer.
      /// </summary>
      /// <param name="buffer">Buffer to encrypt.</param>
      /// <returns>Encrypted buffer.</returns>
      public static byte[] Encrypt(byte[] buffer) {
         return Encrypt(buffer, 0, buffer.Length);
      }

      /// <summary>
      /// Encrypt a byte buffer.
      /// </summary>
      /// <param name="buffer">Buffer to encrypt.</param>
      /// <param name="offset">Offset</param>
      /// <param name="count">Count</param>
      /// <returns>Encrypted buffer.</returns>
      public static byte[] Encrypt(byte[] buffer, int offset, int count) {
         if (s_encryptor == null) {
            throw new InvalidOperationException("Key and IV must be set before you do this.");
         }
         if (buffer == null) {
            throw new ArgumentNullException("buffer");
         }

         return s_encryptor.TransformFinalBlock(buffer, offset, count);
      }

      /// <summary>
      /// Encrypt a byte buffer.
      /// </summary>
      /// <param name="text">Text to encrypt.</param>
      /// <returns>Encrypted text.</returns>
      public static string Encrypt(string text) {
         if (s_encryptor == null) {
            throw new InvalidOperationException("Key and IV must be set before you do this.");
         }
         if (text == null) {
            throw new ArgumentNullException("text");
         }
         if (text.Length <= 0) {
            return string.Empty;
         }

         byte[] buffer = s_encoding.GetBytes(text);
         byte[] encrypted = Encrypt(buffer);

         return Convert.ToBase64String(encrypted);
      }

      /// <summary>
      /// Decrypt a byte buffer.
      /// </summary>
      /// <param name="buffer">Buffer to decrypt.</param>
      /// <returns>Decrypted buffer.</returns>
      public static byte[] Decrypt(byte[] buffer) {
         return Decrypt(buffer, 0, buffer.Length);
      }

      /// <summary>
      /// Decrypt a byte buffer.
      /// </summary>
      /// <param name="buffer">Buffer to decrypt.</param>
      /// <param name="offset">Offset</param>
      /// <param name="count">Count</param>
      /// <returns>Decrypted buffer.</returns>
      public static byte[] Decrypt(byte[] buffer, int offset, int count) {
         if (s_decryptor == null) {
            throw new InvalidOperationException("Key and IV must be set before you do this.");
         }
         if (buffer == null) {
            throw new ArgumentNullException("buffer");
         }

         return s_decryptor.TransformFinalBlock(buffer, offset, count);
      }

      /// <summary>
      /// Decrypt a byte buffer.
      /// </summary>
      /// <param name="text">Text to decrypt.</param>
      /// <returns>Decrypted text.</returns>
      public static string Decrypt(string text) {
         if (s_encryptor == null) {
            throw new InvalidOperationException("Key and IV must be set before you do this.");
         }
         if (text == null) {
            throw new ArgumentNullException("text");
         }
         if (text.Length <= 0) {
            return string.Empty;
         }

         byte[] buffer = Convert.FromBase64String(text);
         byte[] decrypted = Decrypt(buffer);

         return s_encoding.GetString(decrypted);
      }
   }
}